/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.msu.common.manager;

import io.iec.edp.caf.commons.runtime.msu.ServiceUnitConfigService;
import io.iec.edp.caf.commons.runtime.msu.entities.MsuCommonInfo;
import io.iec.edp.caf.app.manager.classloader.CAFClassLoader;
import lombok.var;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.Assert;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.*;

/**
 * @author Leon Huo
 */

public class AppManager {

    private static CAFClassLoader classLoader;

    private static Logger logger = LoggerFactory.getLogger(AppManager.class);

    public static void enableAppManager(Thread thread) {
        if (classLoader != null) {
            return;
        }

        Map<String, String[]> pathMap = new LinkedHashMap<>();

        //获取开启的SU的path,不能getBean，该方法在初始化bean之前就被调用了
        //SpringBeanUtils.getBean(ServiceUnitAwareService.class);
//        ServiceUnitAwareService serviceUnitAwareUtil =  new ServiceUnitAwareServiceImpl();
//        List<ServiceUnitInfo> serviceUnitInfoList = serviceUnitAwareUtil.getAllServiceUnits();

        List<MsuCommonInfo> serviceUnitInfoList = ServiceUnitConfigService.getAllSuInfo();
        Set<String> enabledServiceUnitSet = new LinkedHashSet<>();
//        for (String enabledSUName : serviceUnitAwareUtil.getEnabledServiceUnits()) {
//            enabledServiceUnitSet.add(enabledSUName.toLowerCase());
//        }
        for (String enabledSUName : ServiceUnitConfigService.getEnableSu()) {
            enabledServiceUnitSet.add(enabledSUName.toLowerCase());
        }

        for (MsuCommonInfo serviceUnitInfo : serviceUnitInfoList) {
            String name = serviceUnitInfo.getName().toLowerCase();
            if (enabledServiceUnitSet.contains(name)) {
                if (!pathMap.containsKey(name)){
                    pathMap.put(name, new String[]{serviceUnitInfo.getPath()});
                }
                else {
                    //查找重复的su，提示出来
                    StringBuilder message = new StringBuilder("There is an MSU with the same name:" + serviceUnitInfo.getName() + "，The path is as follows:");
                    for (MsuCommonInfo info : serviceUnitInfoList) {
                        if (name.equalsIgnoreCase(info.getName())) {
                            message.append(info.getPath()).append(",");
                        }
                    }

                    logger.error(message.substring(0, message.length() - 1));
                }
            }
        }
        //构造并替换classloader
        AppManager.classLoader = new CAFClassLoader((URLClassLoader) thread.getContextClassLoader(), pathMap);
        thread.setContextClassLoader(classLoader);
    }

    /**
     * 获取带特定注解的class
     *
     * @param appName
     * @param annotation
     * @return
     */
    public static Set<Class<?>> getClassesWithAnnotation(String appName, Class<? extends Annotation> annotation) {
        if (classLoader == null) {
            return new HashSet<>();
        }

        Set<Class<?>> result = new HashSet<>();
        Set<Class<?>> classSet = classLoader.getClassManager().getAppClasses(appName);

        for (Class clazz : classSet) {
            if (clazz.getAnnotation(annotation) != null) {
                result.add(clazz);
            }
        }
        return result;
    }

    /**
     * 获取带特定注解的class
     *
     * @param annotation
     * @return
     */
    public static Set<Class<?>> getClassesWithAnnotation(Class<? extends Annotation> annotation) {
        if (classLoader == null) {
            return new HashSet<>();
        }

        Set<Class<?>> result = new HashSet<>();
        Set<Class<?>> classSet = classLoader.getClassManager().getAllClasses();
        for (Class clazz : classSet) {
            if (clazz.getAnnotation(annotation) != null) {
                result.add(clazz);
            }
        }
        return result;
    }

    public static Set<String> getBeanNames(String appName, Class<?> targetClass) {
        Assert.notNull(appName, "APPName cannot be empty");
        Set<Class<?>> configurationSet = AppManager.getClassesWithAnnotation(appName, Configuration.class);
        List<String> autoConfigurationList = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader);
        for (String autoConfigurationName : autoConfigurationList) {
            if (appName.equals(classLoader.getClassManager().getAppName(autoConfigurationName))) {
                Class clazz = classLoader.getClassManager().getClass(autoConfigurationName);
                if (clazz == null) {
                    try {
                        clazz = classLoader.loadClass(autoConfigurationName);
                    } catch (ClassNotFoundException e) {
                        logger.info("Configuration class not found" + autoConfigurationName);
                    }
                }

                if (clazz != null) {
                    configurationSet.add(clazz);
                }
            }
        }

        Set<String> resultSet = new HashSet<>();
        for (Class clazz : configurationSet) {
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                Bean beanAnnotation = method.getAnnotation(Bean.class);
                if (beanAnnotation != null && method.getReturnType().equals(targetClass)) {
                    String beanName = method.getName();
                    beanName = beanAnnotation.value().length > 0 ? beanAnnotation.value()[0] : beanName;
                    beanName = beanAnnotation.name().length > 0 ? beanAnnotation.name()[0] : beanName;
                    resultSet.add(beanName);
                }
            }
        }

        return resultSet;
    }

    /**
     * 获取GspService的接口
     *
     * @param clazz 接口集合
     * @return 打了GspEndpoint标签的接口
     */
    private static Class<?> getInterfaceByAnnotation(Class<? extends Annotation> annotation, Class<?> clazz) {
        if (clazz == null)
            return null;

        if (clazz.getName().startsWith("$Proxy") || clazz.getName().indexOf("EnhancerBySpringCGLIB") >= 0) {
            clazz = clazz.getSuperclass();
        }

        if (clazz.getDeclaredAnnotation(annotation) != null)
            return clazz;

        Class<?>[] interfaces = clazz.getInterfaces();
        for (var c : interfaces) {
            var a = c.getDeclaredAnnotation(annotation);
            if (a != null) {
                return c;
            }
        }

        return null;
    }

    /**
     * 获取系统的ClassLoader
     * 一般串行情况下可以该方法
     * @return
     */
    public static CAFClassLoader getCafClassLoader() {
        return classLoader;
    }

}
